//! 管理字典
use super::SysFilter;
use crate::{error::AppResult, extract::Json, state::AppState, AppError};
use crate::{extract::ValJson, security::LoginUser};
use ane_system::dao::{SysDict, SysDictDataDo, SysDictDataVo, SysDictDo, SysDictVo};
use axum::{
    extract::{Path, Query},
    response::IntoResponse,
    routing::{get, put},
    Extension, Router,
};
use common::page::{PageInfo, PageParams};
use loginmanager::AuthUser;
use sea_orm::TransactionTrait;
use serde::Deserialize;

/// [Dict](ane_system::entity::sys_dict)操作相关的路由
/// - `{prefix}/` `GET|POST` [Dict](ane_system::entity::sys_dict)的查询和创建
/// - `{prefix}/:id` `PUT|DELETE` [Dict](ane_system::entity::sys_dict)的更新和删除
/// - `{prefix}/:dict_id/` `GET|POST` [DictData](ane_system::entity::sys_dict_data)的更新和删除
/// - `{prefix}/:dict_id/:id` `PUT|DELETE` [DictData](ane_system::entity::sys_dict_data)的更新和删除
pub fn get_route<S>(prefix: &str) -> Router<S>
where
    S: Sync + Send + Clone + 'static,
{
    let f = move |p: &str| -> String { format!("{}{p}", prefix) };
    #[cfg_attr(rustfmt, rustfmt_skip)]
    Router::<S>::new()
        .route(&f("/"), get(list_dict).post(create))
        .route(&f("/:id"), put(update).delete(delete))
        .route(&f("/:dict_id/"), get(list_dict_data).post(create_dict_data))
        .route(&f("/:dict_id/:id"),put(update_dict_data).delete(del_dict_data),)
}

pub async fn list_dict(
    Extension(state): Extension<AppState>,
    Query(filter): Query<SysFilter>,
) -> AppResult<Json<PageInfo<SysDictVo>>> {
    SysDict::list_dict(
        state.db(),
        filter.page.zero_page(),
        filter.page.page_size(),
        filter.dict_name,
        filter.dict_type,
        filter.status,
    )
    .await
    .map(|(datas, total)| PageInfo::new(datas, &filter.page, total))
    .map(Json)
    .map_err(Into::into)
}

pub async fn create(
    Extension(state): Extension<AppState>,
    AuthUser(user): AuthUser<LoginUser>,
    ValJson(data): ValJson<SysDictDo>,
) -> AppResult<impl IntoResponse> {
    let txn = state.db().begin().await?;
    if SysDict::get_dict_by_type::<SysDictVo>(&txn, &data.dict_type, None)
        .await?
        .is_some()
    {
        Err(AppError::BadParam(format!(
            "dict type `{}` exists",
            data.dict_type
        )))?;
    }
    let res = SysDict::create_dict(&txn, data, user.id)
        .await
        .map(Json)
        .map_err(Into::into);
    txn.commit().await?;
    res
}

pub async fn update(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
    AuthUser(user): AuthUser<LoginUser>,
    ValJson(data): ValJson<SysDictDo>,
) -> AppResult<impl IntoResponse> {
    let txn = state.db().begin().await?;
    if matches!(SysDict::get_dict_by_type::<SysDictVo>(&txn, &data.dict_type, None).await?, Some(r) if r.id!=id)
    {
        Err(AppError::BadParam(format!(
            "dict type `{}` exists",
            data.dict_type
        )))?;
    }
    let res = SysDict::update_dict(&txn, id, data, user.id)
        .await
        .map(Json)
        .map_err(Into::into);
    txn.commit().await?;
    res
}

pub async fn delete(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
) -> AppResult<impl IntoResponse> {
    SysDict::delete_dcit(state.db(), id)
        .await
        .map(Json)
        .map_err(Into::into)
}

pub async fn list_dict_data(
    Extension(state): Extension<AppState>,
    Path(dict_id): Path<i32>,
    Query(page_params): Query<PageParams>,
) -> AppResult<impl IntoResponse> {
    SysDict::list_dict_data_by_id::<SysDictDataVo>(
        state.db(),
        page_params.zero_page(),
        page_params.page_size(),
        dict_id,
        None,
    )
    .await
    .map(|(datas, total)| PageInfo::new(datas, &page_params, total))
    .map(Json)
    .map_err(Into::into)
}

pub async fn create_dict_data(
    Extension(state): Extension<AppState>,
    Path(dict_id): Path<i32>,
    AuthUser(user): AuthUser<LoginUser>,
    ValJson(data): ValJson<SysDictDataDo>,
) -> AppResult<impl IntoResponse> {
    SysDict::create_dict_data::<SysDictDataVo>(state.db(), dict_id, data, user.id)
        .await
        .map(Json)
        .map_err(Into::into)
}

pub async fn update_dict_data(
    Extension(state): Extension<AppState>,
    Path((dict_id, id)): Path<(i32, i32)>,
    AuthUser(user): AuthUser<LoginUser>,
    ValJson(data): ValJson<SysDictDataDo>,
) -> AppResult<impl IntoResponse> {
    SysDict::update_dict_data::<SysDictDataVo>(state.db(), dict_id, id, data, user.id)
        .await
        .map(Json)
        .map_err(Into::into)
}

pub async fn del_dict_data(
    Extension(state): Extension<AppState>,
    Path((dict_id, id)): Path<(i32, i32)>,
) -> AppResult<impl IntoResponse> {
    SysDict::delete_dict_data(state.db(), id)
        .await
        .map(Json)
        .map_err(Into::into)
}
